← OnWeight HQAccessibility Pass

OnWeight — Accessibility & Dynamic Type Pass

A pre-dev review of the balanced theme (the one the All-Screens canvas renders). Covers three things: colour contrast (computed), Dynamic Type / large-text behaviour, and VoiceOver / semantics. Scope note: these screens are a static design mock, so some items (true Dynamic Type scaling, live VoiceOver focus order) are implementation guidance for dev rather than something the mock itself proves. Concrete fixes already applied are marked ✅.


1 · Colour contrast (WCAG 2.1)

Computed against the live balanced-theme tokens. AA needs 4.5:1 for normal text, 3:1 for large text (≥24px, or ≥18.66px bold) and for UI component boundaries / meaningful graphics.

Foreground → backgroundRatioVerdict
ink #15140f → white (primary text)18.4:1✅ AAA
ink → off-white #f9f7f217.2:1✅ AAA
ink-2 #3a3833 → white (secondary)11.7:1✅ AAA
ink-2 → paper-2 card #f1ede610.0:1✅ AAA
ink-3 #524d44 → white (captions)8.4:1✅ AAA
ink-3 → paper-2 card7.2:1✅ AAA
accent #a32a18 → white (links/eyebrows)7.2:1✅ AA
accent → accent-soft tint6.1:1✅ AA
white → accent (button label)7.2:1✅ AA
white → ink (inverted banner/button)18.4:1✅ AAA
t-amber #6f5210 → white7.3:1✅ AA
t-amber → amber-soft6.1:1✅ AA
t-green #2f5a24 → white8.1:1✅ AA
t-green → green-soft6.7:1✅ AA
rule-2 #8a7f68 → white3.95:1⚠️ text-only issue (see below)
rule #e6ded2 → white (hairlines)1.3:1✅ non-text divider (exempt)

Headline: the palette is strong. Every text/icon colour that carries meaning clears AA, most by a wide margin — the warm-paper background does not hurt contrast because the ink ramp is genuinely dark.

The one finding — rule-2 as text. #8a7f68 is 3.95:1: perfectly fine for what it's mostly used for (disclosure chevrons, toggle-off track, unselected radio borders, step dots, drag handles — all UI components / decorative, where the bar is 3:1), but below 4.5:1 if used for readable text.

Not an issue, but worth knowing: meaning is never carried by colour alone — pace verdicts pair the green/amber/red with words ("Made", "Behind"), and the week-streak dots pair colour with a "3 of 7" count. Good. Keep that discipline as new states are added.


2 · Dynamic Type & large-text behaviour

All type in the mock is fixed px (hero numeral 104px, limit 64px, titles 24–28px, body 13–16px, mono eyebrows 10–10.5px). That's correct for a pixel-accurate design artifact, but it means the mock does not itself demonstrate Dynamic Type — that's the single biggest dev translation task here.

Findings / guidance for dev:

collide with the unit or clip at the 393px frame. Use a shrink-to-fit numeral (e.g. width-aware clamp() / minimumScaleFactor) so it never overflows.


3 · VoiceOver / semantics

The mock uses real <button>s for most actions and pairs icons with text labels, which is a good start. The following are dev-implementation notes (a static mock can't fully encode focus order):


Summary for dev

AreaState
Contrast — text & UI✅ Passes AA across the board; placeholder fixed
Contrast — rule-2 as text✅ Fixed (placeholder); keep rule-2 for borders/icons only
Dynamic Type wiring🟡 Dev task — map fixed px → scalable text styles, 11px floor
Hero numeral at large sizes🟠 Dev task — shrink-to-fit + cap growth so it never clips/overflows
Decorative SVG aria-hidden🟡 Dev task — apply kit-wide (pattern set in fc-dash)
Icon-button / tab labels🟡 Dev task — extend the aria-label pattern already on back/close
Chart text alternatives🟠 Dev task — role="img" + summary + hidden data table
Live-region status🟠 Dev task — offline/error/sync banners
Touch targets🟡 Dev task — back/close 38→44pt hit area
Colour-only meaning✅ Already avoided

Bottom line: the colour system is genuinely accessible — the warm palette costs nothing on contrast, and the one text-contrast slip (placeholder) is fixed. The remaining work is dev implementation, not redesign: wire Dynamic Type to scalable styles with a sensible floor and a capped hero, and add the standard semantic layer (aria-hidden on decorative art, labels on icon buttons/tabs, text alternatives for charts, live regions for status, 44pt targets).

OnWeight HQ